﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;


namespace SP.Docs.Migrator
{
    /// <summary>
    /// Export functionality: 
    /// The export operation generates the master schema files which contains all the properties, an index file which contains the list of
    /// all exported documents with their corresponding versions locations. Here's how the export directory looks like
    /// 
    /// index.txt 'list of all exported documents with their relative locations'
    /// _vti_history 'the document versions are stored in this directory with files and their properties schema'
    /// schema.xml 'this file contains the list of all properties which are exported with files. In actual file schema the property value is stored using the id from this file. This is to help in renaming the properties at the destination library if required.
    /// </summary>
    public partial class frmExportImportDocs : Form
    {
        string schemafile = "-schema.xml";
        /// <summary>
        /// This table contains the list of all properties used by document/form or its version also called master schema file
        /// To map the property with a new name for the destination library just change the name in this file.
        /// </summary>
        DataTable schemaTable = new DataTable("masterschema");
        public frmExportImportDocs()
        {
            if (schemaTable.Columns.Count == 0)
            {
                schemaTable.Columns.Add("id");
                schemaTable.Columns.Add("name");
            }
            InitializeComponent();
        }
        private void LogEntry(string msg)
        {
            try
            {
                System.IO.StreamWriter wr = new StreamWriter(Path.Combine(Settings.LogFolder, this.Name + ".txt"), true);
                wr.WriteLine(DateTime.Now + ":" + msg);
                wr.Close();
            }
            catch { }
        }
        #region Master schema file related code
        private void LoadSchema(string filename)
        {
            schemaTable.ReadXml(filename);
        }
        private int AddProperty(string properyname)
        {
            int reutrnVal = schemaTable.Rows.Count;
            schemaTable.Rows.Add(new object[] {schemaTable.Rows.Count, properyname});
            return reutrnVal;
        }
        private int GetProperyIDByName(string name)
        {
            DataRow[] rows = schemaTable.Select("name='" + name + "'");
            if (rows.Length != 0)
                return Convert.ToInt32(rows[0]["id"]);
            else
                return AddProperty(name);
        }
        private void StorePropertySchema(SPListItem itm)
        {
            LogEntry("storing property schema");
            if (schemaTable.Rows.Count == 0)
            {
                foreach (System.Collections.DictionaryEntry entry in itm.Properties)
                {
                    AddProperty(entry.Key.ToString());
                    LogEntry(entry.Key.ToString());
                }
                LogEntry("Created");
                AddProperty("Created");
                LogEntry("CreatedBy");
                AddProperty("CreatedBy");
                LogEntry("Modified");
                AddProperty("Modified");
                LogEntry("ModifiedBy");
                AddProperty("ModifiedBy");
            }
        }
        private string GetPropertyNameByID(int id)
        {
            DataRow[] rows = schemaTable.Select("id='" + id + "'");
            if (rows.Length != 0)
                return rows[0]["name"].ToString();
            return "";
        }
        private DataTable GetSchemaTable()
        {
            DataTable table = new DataTable("schema");
            table.Columns.Add("fieldname");
            table.Columns.Add("value");
            return table;
        }
        #endregion
        #region Export Code
        private void ProcessItem(SPListItem item, string destinationFolder, DataTable table, StreamWriter wr)
        {
            StorePropertySchema(item);
            SPFile file = item.File; 
            #region store versions
            for (int j = 0; j < file.Versions.Count; j++)
            {
                SPFileVersion version = file.Versions[j];
                LogEntry("Version: " + version.Url);
                string versionDir = Path.Combine(destinationFolder, version.Url.Replace(version.File.Name, ""));

                if (!Directory.Exists(versionDir))
                    Directory.CreateDirectory(versionDir);

                versionDir = Path.Combine(destinationFolder, version.Url);

                File.WriteAllBytes(versionDir, version.OpenBinary());

                wr.WriteLine(version.Url);

                table = GetSchemaTable();
                foreach (System.Collections.DictionaryEntry prop in version.Properties)
                {
                    LogEntry(GetProperyIDByName(prop.Key.ToString()).ToString() + " : " + prop.Value);
                    table.Rows.Add(new object[] { GetProperyIDByName(prop.Key.ToString()).ToString(), prop.Value });
                }
                LogEntry("Created : " + version.Created.ToLocalTime());
                table.Rows.Add(GetProperyIDByName("Created"), version.Created.ToLocalTime());
                table.Rows.Add(GetProperyIDByName("CreatedBy"), version.CreatedBy.LoginName);
                LogEntry("CreatedBy : " + version.CreatedBy.LoginName);
                table.Rows.Add(GetProperyIDByName("Modified"), version.Created.ToLocalTime());
                LogEntry("Modified : " + version.Created.ToLocalTime());
                table.Rows.Add(GetProperyIDByName("ModifiedBy"), version.CreatedBy.LoginName);
                LogEntry("ModifiedBy : " + version.CreatedBy.LoginName);

                table.WriteXml(versionDir + schemafile);
            }
            #endregion
            #region store current version
            if (item.File != null)
            {
                File.WriteAllBytes(Path.Combine(destinationFolder, item.Name), item.File.OpenBinary());
                wr.WriteLine(item.Name);
            }
            LogEntry("Processing actual file");
            table.Rows.Clear();
            table = GetSchemaTable();
            foreach (System.Collections.DictionaryEntry prop in item.Properties)
            {
                try
                {
                    LogEntry(GetProperyIDByName(prop.Key.ToString()).ToString() + " : " + prop.Value);
                    table.Rows.Add(new object[] { GetProperyIDByName(prop.Key.ToString()).ToString(), prop.Value });
                }
                catch(Exception e) 
                {
                    LogEntry("Error while processing properties: " + e.Message);
                }
            }
            try
            {
                LogEntry(GetProperyIDByName("Created") + " : " + item.File.TimeCreated.ToLocalTime());
                table.Rows.Add(GetProperyIDByName("Created"), item.File.TimeCreated.ToLocalTime());
                LogEntry(GetProperyIDByName("CreatedBy") + " : " + item.File.Author.LoginName);
                table.Rows.Add(GetProperyIDByName("CreatedBy"), item.File.Author.LoginName);
                LogEntry(GetProperyIDByName("Modified") + " : " + item.File.TimeLastModified.ToLocalTime());
                table.Rows.Add(GetProperyIDByName("Modified"), item.File.TimeLastModified.ToLocalTime());

                LogEntry(GetProperyIDByName("ModifiedBy") + " : " + item.File.ModifiedBy.LoginName);

                table.Rows.Add(GetProperyIDByName("ModifiedBy"), item.File.ModifiedBy.LoginName);
            }
            catch (Exception e)
            {
                LogEntry("Error while processing properties: " + e.Message);
            }
            try
            {
                table.WriteXml(Path.Combine(destinationFolder, item.Name + schemafile));
            }
            catch (Exception e)
            {
                LogEntry("exception occured while processing file: " + e.Message);
            }
            #endregion
        }
        private void btnExport_Click(object sender, EventArgs e)
        {
            System.Windows.Forms.Cursor.Current = System.Windows.Forms.Cursors.WaitCursor;
            LogEntry("loading ids");
            LogEntry("preparing index file");
            string destinationFolder = this.txtFolderDr.Text;
            StreamWriter wr = new StreamWriter(destinationFolder + "\\index.txt");
            DataTable table = GetSchemaTable();
            try
            {
                LogEntry("connecting to a site");
                using (SPSite site = new SPSite(this.txtSiteUrl.Text))
                {
                    LogEntry("connecting to a web");
                    using (SPWeb web = site.OpenWeb())
                    {
                        LogEntry("loading list");
                        SPList list = web.Lists[comboLists.Text];
                        
                        if (!chkBxAll.Checked)
                        {
                            string[] ids = this.txtIds.Text.Split(new char[] { ',' });
                            LoadProgressBar(ids.Length);
                            for (int i = 0; i < ids.Length; i++)
                            {
                                SPListItem item = list.GetItemById(Convert.ToInt32(ids[i]));
                                ProcessItem(item, destinationFolder, table, wr);
                                SetProgress(i + 1);
                            }
                        }
                        else
                        {
                            LoadProgressBar(list.Items.Count);
                            int progVariable = 1;
                            foreach (SPListItem item in list.Items)
                            {
                                SetProgress(progVariable++);
                                ProcessItem(item, destinationFolder, table, wr);

                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                LogEntry(ex.Message + ":" + ex.StackTrace);
            }
            finally
            {
                if (wr != null)
                    wr.Close();
                schemaTable.WriteXml(Path.Combine(destinationFolder, "schema.xml"));
                LogEntry("Export completed");
                System.Windows.Forms.Cursor.Current = System.Windows.Forms.Cursors.Default;
            }
        }
        #endregion
        #region events
        private void btnCancel_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void btnBrowse_Click(object sender, EventArgs e)
        {
            System.Windows.Forms.Cursor.Current = System.Windows.Forms.Cursors.WaitCursor;
            try
            {
                using (SPSite site = new SPSite(this.txtSiteUrl.Text))
                {
                    using (SPWeb web = site.OpenWeb())
                    {
                        foreach (SPList list in web.Lists)
                        {
                            comboLists.Items.Add(list.Title);
                        }
                    }
                }
            }
            catch(Exception ex) 
            {
                MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            System.Windows.Forms.Cursor.Current = System.Windows.Forms.Cursors.Default;
        }

        private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            System.Windows.Forms.Cursor.Current = System.Windows.Forms.Cursors.WaitCursor;
            try
            {
                if (!string.IsNullOrEmpty(comboLists.Text))
                {
                    using (SPSite site = new SPSite(this.txtSiteUrl.Text))
                    {
                        using (SPWeb web = site.OpenWeb())
                        {
                            dataGridListItems.DataSource = web.Lists[comboLists.Text].Items.GetDataTable();
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            System.Windows.Forms.Cursor.Current = System.Windows.Forms.Cursors.Default;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            if (folderBrowseDlg.ShowDialog() == DialogResult.OK)
                this.txtFolderDr.Text = folderBrowseDlg.SelectedPath;
        }
        #endregion
        #region Import Code
        private void RestoreFile(string file, string sourceFile, SPFileCollection targetFiles,  SPWeb web)
        {
            DataTable table = GetSchemaTable();
            table.ReadXml(Path.Combine(txtFolderDr.Text, sourceFile + schemafile));

            DateTime created = Convert.ToDateTime(((DataRow[])table.Select("fieldname='" + GetProperyIDByName("Created") + "'"))[0]["value"].ToString());
            DateTime modified = Convert.ToDateTime(((DataRow[])table.Select("fieldname='" + GetProperyIDByName("Modified") + "'"))[0]["value"].ToString());
            SPUser createdBy = web.SiteUsers[((DataRow[])table.Select("fieldname='" + GetProperyIDByName("CreatedBy") + "'"))[0]["value"].ToString()];
            SPUser modifiedBy = web.SiteUsers[((DataRow[])table.Select("fieldname='" + GetProperyIDByName("ModifiedBy") + "'"))[0]["value"].ToString()];

            StreamReader temp = new StreamReader(sourceFile);

            string data = temp.ReadToEnd();

            byte[] buffer = ASCIIEncoding.Default.GetBytes(data);

            SPFile newFile = targetFiles.Add(file, buffer, createdBy, modifiedBy, created, modified);

            for (int i = 0; i < table.Rows.Count; i++)
            {
                try
                {
                    newFile.Properties[GetPropertyNameByID(Convert.ToInt32(table.Rows[i]["fieldname"]))] = table.Rows[i]["value"].ToString();
                }
                catch (Exception ex) { LogEntry(ex.Message + ":" + " The propery name '" + GetPropertyNameByID(Convert.ToInt32(table.Rows[i]["fieldname"])) + "' cannot be restored for file '" + file + "'"); }
            }
            newFile.Item["Created"] = created;
            newFile.Item["Modified"] = modified;
            newFile.Item.UpdateOverwriteVersion();
        }
        private void btnImport_Click(object sender, EventArgs e)
        {
            System.Windows.Forms.Cursor.Current = System.Windows.Forms.Cursors.WaitCursor;
            LogEntry("Connecting to a site");
            using (SPSite site = new SPSite(this.txtSiteUrl.Text))
            {
                LogEntry("Connecting to a web");
                using (SPWeb web = site.OpenWeb())
                {
                    LogEntry("loading source files");
                    SPFileCollection targetFiles = web.Lists[comboLists.Text].RootFolder.Files;
                    LogEntry("loading schema");
                    LoadSchema(Path.Combine(txtFolderDr.Text, "schema.xml"));
                    StreamReader reader = new StreamReader(Path.Combine(txtFolderDr.Text, "index.txt"));
                    try
                    {
                        List<string> fileIndex = new List<string>();
                        while (!reader.EndOfStream)
                        {
                            try
                            {
                                fileIndex.Add(reader.ReadLine());
                            }
                            catch (Exception ex) { LogEntry(ex.Message + ":" + ex.StackTrace); }
                        }
                        LoadProgressBar(fileIndex.Count);
                        for (int i = 0; i < fileIndex.Count; i++)
                        {
                            try
                            {
                                string file = fileIndex[i];
                                SetProgress(i + 1);
                                string sourceFile = Path.Combine(txtFolderDr.Text, file);
                                if (!file.Contains("_vti_history"))
                                    RestoreFile(file, sourceFile, targetFiles, web);
                                else
                                    RestoreFile(targetFiles.Folder.Url + "/" + file.Substring(file.LastIndexOf("/") + 1, file.Length - file.LastIndexOf("/") - 1), sourceFile, targetFiles, web);
                            }
                            catch (Exception ex) { LogEntry(ex.Message + ":" + ex.StackTrace); }
                        }
                    }
                    finally
                    {
                        if (reader != null)
                            reader.Close();
                    }
                }
            }
            LogEntry("Import completed");
            System.Windows.Forms.Cursor.Current = System.Windows.Forms.Cursors.Default;
        }
        #endregion
        private void LoadProgressBar(int count)
        {
            this.progBar.Maximum = count;
            this.progBar.Minimum = 0;
            this.progBar.Value = 0;
        }
        private void SetProgress(int value)
        {
            this.progBar.Value = value;
            this.progBar.Update();
        }
        private void chkBxAll_CheckedChanged(object sender, EventArgs e)
        {
            if (this.chkBxAll.Checked)
                this.txtIds.Enabled = false;
            else
                this.txtIds.Enabled = true;
        }
    }
}
